#include "passxml.h"

#define PASS_CONFIG_DBG

#ifdef PASS_CONFIG_DBG
#include <iostream>
using namespace std;
FILE *xml_dbg_fp = NULL;
#endif

const unsigned int NUM_INDENTS_PER_SPACE=2;
// a utility function defining a very simple method to indent a line of text
const char * getIndent( unsigned int numIndents )
{
    static const char * pINDENT = "                                      + ";
    static const unsigned int LENGTH = (unsigned int)strlen( pINDENT );

    if ( numIndents > LENGTH ) numIndents = LENGTH;

    return &pINDENT[ LENGTH-numIndents ];
}

// same as getIndent but no "+" at the end
const char * getIndentAlt( unsigned int numIndents )
{
	static const char * pINDENT="                                        ";
	static const unsigned int LENGTH=(unsigned int)strlen( pINDENT );
	unsigned int n=numIndents*NUM_INDENTS_PER_SPACE;
	if ( n > LENGTH ) n = LENGTH;

	return &pINDENT[ LENGTH-n ];
}

int dump_attribs_to_file(FILE *fp, TiXmlElement* pElement, unsigned int indent)
{
	if ( !pElement ) return 0;

	TiXmlAttribute* pAttrib=pElement->FirstAttribute();
	int i=0;
	int ival;
	double dval;
	const char* pIndent=getIndent(indent);
	fprintf(fp, "\n");
	while (pAttrib)
	{
		fprintf(fp, "%s%s: value=[%s]", pIndent, pAttrib->Name(), pAttrib->Value());

		if (pAttrib->QueryIntValue(&ival)==TIXML_SUCCESS)    fprintf(fp, " int=%d", ival);
		if (pAttrib->QueryDoubleValue(&dval)==TIXML_SUCCESS) fprintf(fp, " d=%1.1f", dval);
		fprintf(fp, "\n" );
		i++;
		pAttrib=pAttrib->Next();
	}
	return i;	
}

void dump_to_file( TiXmlNode * pParent, unsigned int indent = 0 )
{
    if ( !pParent ) return;
	int num = 0;
	FILE *fp = fopen("xml_dump.txt", "a");

    TiXmlText *pText;
    int t = pParent->Type();
    fprintf(fp, "%s", getIndent( indent));

    switch ( t )
    {
    case TiXmlNode::TINYXML_DOCUMENT:
        fprintf(fp, "Document" );
        break;

    case TiXmlNode::TINYXML_ELEMENT:
        fprintf(fp, "Element \"%s\"", pParent->Value() );
		num=dump_attribs_to_file(fp, pParent->ToElement(), indent+1);
		switch(num){
			case 0:  printf( " (No attributes)"); break;
			case 1:  printf( "%s1 attribute", getIndentAlt(indent)); break;
			default: printf( "%s%d attributes", getIndentAlt(indent), num); break;
		}
        break;

    case TiXmlNode::TINYXML_COMMENT:
        fprintf(fp, "Comment: \"%s\"", pParent->Value());
        break;

    case TiXmlNode::TINYXML_UNKNOWN:
        fprintf(fp, "Unknown" );
        break;

    case TiXmlNode::TINYXML_TEXT:
        pText = pParent->ToText();
        fprintf(fp, "Text: [%s]", pText->Value() );
        break;

    case TiXmlNode::TINYXML_DECLARATION:
        fprintf(fp, "Declaration" );
        break;
    default:
        break;
    }
    fprintf(fp, "\n" );
	fclose(fp);
    TiXmlNode * pChild;

    for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) 
    {
        dump_to_file( pChild, indent+2 );
    }
}
//=====================================

//get attributes, if attrib_num is less than the real attributes, it just pass attrib_num attributes.
int pass_attribs(TiXmlElement* pElement, struct attribute *attrib, int attrib_num)
{
	int index=0;
	if ( !pElement ) return 0;
	if(attrib == NULL) return 0;

	TiXmlAttribute* pAttrib=pElement->FirstAttribute();

	while (pAttrib)
	{
		strcpy(attrib[index].value, pAttrib->Value());
		strcpy(attrib[index].name, pAttrib->Name());
#ifdef PASS_CONFIG_DBG
		fprintf(xml_dbg_fp, "\t%s = %s", attrib[index].name, attrib[index].value);
#endif
		index++;
		pAttrib=pAttrib->Next();
//		if(index >= attrib_num)
//			break;
	}
#ifdef PASS_CONFIG_DBG
		fprintf(xml_dbg_fp, " (index = %d)\n", index);
#endif
	return index;
}

int pass_days(TiXmlNode * pParent)
{
    TiXmlNode * pChild = pParent;
    int t = pChild->Type();
	char *element_p = NULL;
	struct attribute attrib[DAY_ATTRIB_NUM];
	while(pChild){
		if(t == TiXmlNode::TINYXML_ELEMENT){
			element_p = (char *)pChild->Value();
			if(strcmp(element_p, "day") == 0){
#ifdef PASS_CONFIG_DBG
				fprintf(xml_dbg_fp, "Pass Day:\n");
#endif
				int attrib_num = pass_attribs(pChild->ToElement(), (struct attribute *)attrib, DAY_ATTRIB_NUM);
				if(attrib_num != DAY_ATTRIB_NUM)
					return 0;
				if(strcmp((char *)attrib[0].name, "name") != 0)
					return 0;
				if(strcmp((char *)attrib[1].name, "work") != 0)
					return 0;
#ifdef PASS_CONFIG_DBG
				cout << attrib[0].name << attrib[0].value << endl;
				cout << attrib[1].name << attrib[1].value << endl;
#endif
			}
		}
		pChild = pChild->NextSibling();
	}
	return 1;
}

int pass_persons(TiXmlNode * pParent)
{
    TiXmlNode * pChild = pParent;
    int t = pChild->Type();
	char *element_p = NULL;
	struct attribute attrib[PERSON_ATTRIB_NUM];
	while(pChild){
		if(t == TiXmlNode::TINYXML_ELEMENT){
			element_p = (char *)pChild->Value();
			if(strcmp(element_p, "p") == 0){
#ifdef PASS_CONFIG_DBG
				fprintf(xml_dbg_fp, "Pass Persion:\n");
#endif
				int attrib_num = pass_attribs(pChild->ToElement(), (struct attribute *)attrib, PERSON_ATTRIB_NUM);
				if(attrib_num != PERSON_ATTRIB_NUM)
					return 0;
				if(strcmp((char *)attrib[0].name, "name") != 0)
					return 0;
				if(strcmp((char *)attrib[1].name, "age") != 0)
					return 0;
				if(strcmp((char *)attrib[2].name, "sex") != 0)
					return 0;
#ifdef PASS_CONFIG_DBG
				cout << attrib[0].name << attrib[0].value << endl;
				cout << attrib[1].name << attrib[1].value << endl;
				cout << attrib[2].name << attrib[2].value << endl;
#endif
			}
		}
		pChild = pChild->NextSibling();
	}
	return 1;
}

//pass one section
int pass_section(TiXmlNode * pParent)
{
	if ( !pParent ) return 0;

    TiXmlText *pText = NULL;
	char *element_p = NULL;
	char *test_p = NULL;
    int t = pParent->Type();
	int result = 0;

	struct attribute attrib;

    switch ( t ){
	case TiXmlNode::TINYXML_ELEMENT:
		element_p = (char *)pParent->Value();
		if(strcmp(element_p, "section") == 0){
#ifdef PASS_CONFIG_DBG
			fprintf(xml_dbg_fp, "Pass Section:\n");
#endif
			int attrib_num = pass_attribs(pParent->ToElement(), &attrib, SECTION_ATTRIB_NUM);
			if(attrib_num != SECTION_ATTRIB_NUM)
				return 0;
			if(strcmp((char *)attrib.name, "name") != 0)
				return 0;
			if(strcmp((char *)attrib.value, "days") == 0){
				result = pass_days(pParent->FirstChild());
			}else if(strcmp((char *)attrib.value, "all persons") == 0){
				result = pass_persons(pParent->FirstChild());
			}
		}
        break;
    case TiXmlNode::TINYXML_DOCUMENT:
    case TiXmlNode::TINYXML_COMMENT:
    case TiXmlNode::TINYXML_UNKNOWN:
    case TiXmlNode::TINYXML_TEXT:
    case TiXmlNode::TINYXML_DECLARATION:
    default:
        break;
    }
	return result;
}
int pass_file(TiXmlNode * pParent)
{
    TiXmlNode * pChild;
    int t = pParent->Type();
	char *element_p = NULL;
	int result = 0;

    for ( pChild = pParent->FirstChild(); pChild != 0; pChild = pChild->NextSibling()) 
    {
		element_p = (char *)pChild->Value();
		if(strcmp(element_p, "section") == 0){
			result = pass_section(pChild);
			if(result == 0)
				return 0;
		}
    }
	return 1;
}

int PassConfig()
{
#ifdef PASS_CONFIG_DBG
	xml_dbg_fp = fopen("xml_dbg.txt", "w");
#endif
    TiXmlDocument doc( "model.config" );
    bool loadOkay = doc.LoadFile();
    if ( loadOkay ){
#ifdef PASS_CONFIG_DBG
        dump_to_file( &doc );
#endif
    }else{
        // load failed
#ifdef PASS_CONFIG_DBG
		fprintf(xml_dbg_fp, "load failed.\n");
#endif
		return 0;
    }

	int result = pass_file(&doc);
#ifdef PASS_CONFIG_DBG
	fclose(xml_dbg_fp);
#endif
	return result;
}

#ifdef PASS_CONFIG_DBG
int main()
{
	return PassConfig();
}
#endif